﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Controls.DataVisualization.Charting;
using FinPlusCommon;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusWPFControls
{
    public partial class FinPlusChartMovingAverage: FinPlusControl
    {
        private IFinPlusComp _conn;
        private IFunctions _funcs;
        private readonly string[] _fields, _titles;
        private readonly string[][] _averages;

        //construct
        public FinPlusChartMovingAverage(IFinPlusComp connection, string[] fields, string[] titles, string averages)
        {
            InitializeComponent();
            _fields = fields;
            _titles = titles;
            _averages = (string[][])u.ToJaggedArray(averages, ';', ',');
            IsValid = true;
            AddConnection((FinPlusConnectService)connection);
        }

        //common control interface
        public override void Dispose()
        {
            try
            {
                if (_conn != null)
                    _conn.Dispose();
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Warning, Id, Config, e.Message, e);
            }
        }

        //private
        private async void AddConnection(FinPlusConnectService conn)
        {
            try
            {
                 _conn = conn;
                 _funcs = conn.GetFunctions();
                 
                 var dataPoints = await Task.Run(() => { return new MovingAverageDataPoints(_funcs, _fields, _averages); });
                 var n = 0;

                 foreach (var average in _averages)
                 {
                     var field = average[0];
                     chart.Series.Add(GetSeries(dataPoints, "DateTime", average[0], BrushHelper.StringToBrush(average[2]), field, false));
                     chart.Series.Add(GetSeries(dataPoints, "DateTime", string.Format("Average{0}", ++n), BrushHelper.StringToBrush(average[2]), string.Format("{0} Interval({1})", field, TimeSpan.Parse(average[1])) , true));
                 }
             
                 chart.Title = ChartFormaters.GetTitle(_funcs.Values.First(), _titles, TimeSpan.MinValue,  dataPoints.Start, dataPoints.End, "dd-MM-yy H:mm");
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private LineSeries GetSeries(MovingAverageDataPoints dataPoints, string independant, string dependant, Brush lineColor, string title, bool isDashedLine)
        {
            var series = new LineSeries() {ItemsSource = dataPoints.Values.ToList(), DependentValueBinding = new Binding(dependant), 
                IndependentValueBinding = new Binding(independant), Foreground = lineColor, Title = title};

            if (isDashedLine) series.PolylineStyle = ChartFormaters.GetDashedLineStyle();

            return series;
        }

        //private classes
        private class MovingAverageDataPoints : SortedDictionary<int, MovingAverageDataPoint>
        {
            public DateTime Start, End;

            private IFunctions _funcs;
            private string[] _fields;
            private string[][] _averages;

            public MovingAverageDataPoints(IFunctions funcs, string[] fields, string[][] averages)
            {
                _fields = fields;
                _funcs = funcs;
                _averages = averages;
                Initialise();
                SetAverages();
            }

            //private
            private void Initialise()
            {
                foreach (var type in _fields)
                {
                    var res = _funcs.Where(string.Format("Name='Info' And Value='{0}'", type));
                    if (res.Count() < 1) continue;

                    foreach (var item in res.First().Value)
                    {
                        if (item.Index < 7) continue;

                        MovingAverageDataPoint dataPoint;
                        var key = int.Parse(item.Name);
                        if (!this.ContainsKey(key))
                            this[key] = dataPoint = new MovingAverageDataPoint();
                        else
                            dataPoint = this[key];

                        if (Start.Equals(DateTime.MinValue)) Start = dataPoint.DateTime;

                        dataPoint.Add(item, type);
                    }
                }

                End = this.Last().Value.DateTime;
            }

            private void SetAverages()
            {
                var n = 1;
                foreach (var average in _averages)
                    SetAverage(average, n++);
            }

            private void SetAverage(string[] average, int n)
            {
                var start = DateTime.MinValue;
                var interval = TimeSpan.Parse(average[1]);
                var queue = new Queue<MovingAverageDataPoint>();
                var field = average[0];
                double total = 0;
                
                foreach (var dataPoint in this.Values)
                {
                    if (start.Equals(DateTime.MinValue)) start = dataPoint.DateTime + interval;

                    queue.Enqueue(dataPoint);
                    total += dataPoint.Get(field);
                    
                    if (dataPoint.DateTime <= start)
                    {
                        dataPoint.SetAverage(dataPoint.Get(field), n);
                        continue;
                    }

                    do
                    {
                        if (queue.Count() < 1 || queue.Peek().DateTime < dataPoint.DateTime - interval) break;

                        total -= queue.Take(1).First().Get(field);
                    } 
                    while (true);

                    dataPoint.SetAverage(total / queue.Count(), n);
                }
            }
        }

        private class MovingAverageDataPoint : ChartDataPoint
        {
            public double Average1 { get; set; }
            public double Average2 { get; set; }
            public double Average3 { get; set; }
            public double Average4 { get; set; }
            public double Average5 { get; set; }
            public double Average6 { get; set; }

            public void SetAverage(double value, int n)
            {
                switch (n)
                {
                    case 1: Average1 = value; break;
                    case 2: Average2 = value; break;
                    case 3: Average3 = value; break;
                    case 4: Average4 = value; break;
                    case 5: Average5 = value; break;
                    case 6: Average6 = value; break;
                }
            }
        }
    }
}
